unit QImport2ASCII;

{$I VerCtrl.inc}

interface

uses Classes, QImport2, SysUtils, IniFiles;

type
  TQImport2ASCII = class(TQImport2)
  private
    FComma: char;
    FQuote: char;
    //---
    FFile: TextFile;
    FColumns: TStrings;
    FCounter: integer;
    FBuffStr: string;
    //---
    function HasComma: boolean;
    procedure ReadColumns(const Str: string; AStrings: TStrings);
  protected
    function CheckProperties: boolean; override;

    procedure StartImport; override;
    function CheckCondition: boolean; override;
    function Skip: boolean; override;
    procedure FillImportRow; override;
    function ImportData: TQImportResult; override;
    procedure ChangeCondition; override;
    procedure FinishImport; override;

    procedure DoLoadConfiguration(IniFile: TIniFile); override;
    procedure DoSaveConfiguration(IniFile: TIniFile); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property FileName;
    property SkipFirstRows default 0;

    property Comma: char read FComma write FComma;
    property Quote: char read FQuote write FQuote;
  end;

implementation

uses {$IFDEF WIN32}QImport2StrIDs{$ENDIF}
     {$IFDEF LINUX}QImport2Consts{$ENDIF}, DB, QImport2Common;

{ TQImport2ASCII }

constructor TQImport2ASCII.Create(AOwner: TComponent);
begin
  inherited;
  SkipFirstRows := 0;
  FComma := GetListSeparator;
  FQuote := '"';
end;

function TQImport2ASCII.CheckProperties: boolean;
var
  i, j: integer;
begin
  Result := inherited CheckProperties;
  if HasComma then begin // example field=2
    for i := 0 to Map.Count - 1 do
      try
        StrToInt(Map.Values[Map.Names[i]]);
      except
        on E:EConvertError do raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_MapMissing){$ENDIF}
                                                         {$IFDEF LINUX}QIE_MapMissing{$ENDIF});
      end;
  end
  else begin // example: field=3;5
    for i := 0 to Map.Count - 1 do begin
      j := Pos(';', Map.Values[Map.Names[i]]);
      if j = 0 then raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_MapMissing){$ENDIF}
                                               {$IFDEF LINUX}QIE_MapMissing{$ENDIF})
      else begin
        try
          StrToInt(Copy(Map.Values[Map.Names[i]], 1, j - 1));
        except
          on E:EConvertError do raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_MapMissing){$ENDIF}
                                                           {$IFDEF LINUX}QIE_MapMissing{$ENDIF});
        end;
        try
          StrToInt(Copy(Map.Values[Map.Names[i]], j + 1, Length(Map.Values[Map.Names[i]]) - j));
        except
          on E:EConvertError do raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_MapMissing){$ENDIF}
                                                           {$IFDEF LINUX}QIE_MapMissing{$ENDIF});
        end;
      end;
    end;
  end
end;

procedure TQImport2ASCII.StartImport;
begin
  AssignFile(FFile, FileName);
  Reset(FFile);
  FColumns := TStringList.Create;
  FCounter := 0;
end;

function TQImport2ASCII.CheckCondition: boolean;
begin
  Result := not Eof(FFile);
end;

function TQImport2ASCII.Skip: boolean;
begin
  Readln(FFile, FBuffStr);
  Result := (SkipFirstRows > 0) and (FCounter < SkipFirstRows);
  if Result then Inc(FCounter);
  Result := Result or (Trim(FBuffStr) = EmptyStr);
end;

procedure TQImport2ASCII.FillImportRow;
var
  j, k: integer;
begin
  FImportRow.ClearValues;
  ReadColumns(FBuffStr, FColumns);
  for j := 0 to FImportRow.Count - 1 do begin
    k := Map.IndexOfName(FImportRow[j].Name);
    if (k > -1) and
      (IsCSV or (QImportDestinationFindColumn(IsCSV, ImportDestination, DataSet,
          {$IFNDEF NOGUI}DBGrid, ListView, StringGrid, GridCaptionRow,{$ENDIF}
	    FImportRow[j].Name) > -1) or
        (ImportDestination = qidUserDefined)) then
    begin
      try
        if HasComma
          then FBuffStr := FColumns[StrToInt(Map.Values[Map.Names[k]]) - 1]
          else FBuffStr := FColumns.Values[Map.Names[k]];
      except
        FBuffStr := EmptyStr;
      end;
      FImportRow.SetValue(Map.Names[k], FBuffStr, false);
    end;
    DoUserDataFormat(FImportRow[j]);
  end;
end;

function TQImport2ASCII.ImportData: TQImportResult;
begin
  Result := qirOk;
  try
    try
      if Canceled and not CanContinue then
      begin
        Result := qirBreak;
        Exit;
      end;

      DataManipulation;
      
    except
      on E:Exception do begin
        try
          DestinationCancel;
        except
        end;
        DoImportError(E);
        Result := qirContinue;
        Exit;
      end;
    end;
  finally
    if (not IsCSV) and (CommitRecCount > 0) and not CommitAfterDone and
       ((ImportedRecs + ErrorRecs) mod CommitRecCount = 0) then
      DoNeedCommit;
    if (ImportRecCount > 0) and
       ((ImportedRecs + ErrorRecs) mod ImportRecCount = 0) then
      Result := qirBreak;
  end;
end;

procedure TQImport2ASCII.ChangeCondition;
begin
//
end;

procedure TQImport2ASCII.FinishImport;
begin
  try
    if not Canceled and not IsCSV then
    begin
      if CommitAfterDone then
        DoNeedCommit
      else if (CommitRecCount > 0) and ((ImportedRecs + ErrorRecs) mod CommitRecCount > 0) then
        DoNeedCommit;
    end;
  finally
    if Assigned(FColumns) then FColumns.Free;
    CloseFile(FFile);
  end;
end;

procedure TQImport2ASCII.DoLoadConfiguration(IniFile: TIniFile);
begin
  inherited;
  with IniFile do begin
    SkipFirstRows := ReadInteger(ASCII_OPTIONS, ASCII_SKIP_LINES, SkipFirstRows);
    Comma := Str2Char(ReadString(ASCII_OPTIONS, ASCII_COMMA,
      Char2Str(Comma)), Comma);
    Quote := Str2Char(ReadString(ASCII_OPTIONS, ASCII_QUOTE,
      Char2Str(Quote)), Quote);
  end;
end;

procedure TQImport2ASCII.DoSaveConfiguration(IniFile: TIniFile);
begin
  inherited;
  with IniFile do begin
    WriteInteger(ASCII_OPTIONS, ASCII_SKIP_LINES, SkipFirstRows);
    WriteString(ASCII_OPTIONS, ASCII_COMMA, Comma);
    WriteString(ASCII_OPTIONS, ASCII_QUOTE, Quote);
  end;
end;

function TQImport2ASCII.HasComma: boolean;
begin
  Result := FComma <> #0;
end;

procedure TQImport2ASCII.ReadColumns(const Str: string; AStrings: TStrings);
var
  SS: string;
  P, L, i: integer;
begin
  if HasComma then begin
    CSVStringToStrings(Str, Quote, Comma, AStrings);
  end
  else begin // fixed width
    AStrings.Clear;
    for i := 0 to Map.Count - 1 do begin
      SS := Map.Values[Map.Names[i]];
      P := StrToIntDef(Copy(SS, 1, Pos(';', SS) - 1), 0);
      L := StrToIntDef(Copy(SS, Pos(';', SS) + 1, Length(SS)), 0);
      AStrings.Values[Map.Names[i]] := Copy(Str, P + 1, L);
    end;
  end;
end;

end.
